package com.googlecode.mastercrud.persistence.jpa;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.Query;

import com.googlecode.mastercrud.exceptions.DAOException;
import com.googlecode.mastercrud.persistence.Criterias;
import com.googlecode.mastercrud.persistence.GenericDAO;
import com.googlecode.mastercrud.persistence.Result;

public class JPAGenericDAO<E> implements GenericDAO<E> {
	
	protected Class<E> clazz;
	
	private EntityManager em;
	
	@SuppressWarnings("unchecked")
	public JPAGenericDAO() {
		clazz = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];		
	}

	@Override
	public void delete(E entidade) throws DAOException {
		em.remove(entidade);
	}

	@Override
	public void insert(E entidade) throws DAOException {
		em.persist(entidade);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Result<E> list(Criterias criteria) throws DAOException {
		Query query = em.createQuery(criteria.getQuery());
		if ( criteria.getMaxResults() > 0 )
			query.setMaxResults(criteria.getMaxResults());
		if ( criteria.getFirstResult() > 0 )
			query.setFirstResult(criteria.getFirstResult());
		else query.setFirstResult(0);
		List<E> r = query.getResultList();
		Result<E> result = new Result<E>();
		result.setResult(r);
		return result;
	}

	@Override
	public Result<E> listByCriteria(Criterias criteria) throws DAOException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Result<E> listByExample(E entidade, Criterias criteria) throws DAOException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public E load(Serializable id) throws DAOException {
		return (E) em.find(clazz, id);
	}

	@Override
	public void save(E entity) throws DAOException {
		if ( getEntityID(entity) == null ) {
			this.insert(entity);
		} else {
			this.update(entity);
		}	
	}

	@SuppressWarnings("all")
	private Object getEntityID(E e) throws DAOException {
		for (Field field : e.getClass().getDeclaredFields()) {
			if (field.isAnnotationPresent(Id.class)) {
				try {
					Method method = e.getClass().getMethod("get" + String.valueOf(field.getName().charAt(0)).toUpperCase() + field.getName().substring(1), null);
					Object o = method.invoke(e, null);
					return o;
				} catch (SecurityException e1) {
					throw new DAOException(e1);
				} catch (NoSuchMethodException e1) {
					throw new DAOException(e1);
				} catch (IllegalArgumentException e1) {
					throw new DAOException(e1);
				} catch (IllegalAccessException e1) {
					throw new DAOException(e1);
				} catch (InvocationTargetException e1) {
					throw new DAOException(e1);
				}
			}
		}
		return null;
	}

	@Override
	public void update(E entidade) throws DAOException {
		em.merge(entidade);
	}

}
